/**
	Pulsar engine. Additional include file.
	Own arrays declaration.

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.
		
	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

namespace pl
{

/*
	Safery array
*/
template <class T, pl_uint32 N> class Array
{
public:
	T& operator [](int index)
	{
		PL_ASSERTE(index >=0 && index < (int)N, PL_T("Index out of range")); 
		return *(_array + index); 
	}

	T& operator [](pl_uint32 index)
	{
		PL_ASSERTE(index < N, PL_T("Index out of range")); 
		return *(_array + index); 
	}

	const T& operator [](int index) const
	{
		PL_ASSERTE(index >=0 && index < (int)N, PL_T("Index out of range")); 
		return *(_array + index);
	}

	const T& operator [](pl_uint32 index) const
	{
		PL_ASSERTE(index < N, PL_T("Index out of range")); 
		return *(_array + index);
	}

	const T* get() const
	{
		return _array;
	}

	const T* operator +(int offset)
	{
		PL_ASSERTE(offset >=0 && offset < (int)N, PL_T("Index out of range")); 
		return (_array + offset); 
	}

	const T* operator +(pl_uint32 offset)
	{
		PL_ASSERTE(offset < N, PL_T("Index out of range")); 
		return (_array + offset); 
	}

	inline pl_uint32 capacity() const
	{
		return N; 
	}

protected:
	T _array[N];
};

/*
	Fixed capaticy vector based on Array
*/
template <class T, pl_uint32 N> class Vector
{
public:
	typedef T*			iterator;
	typedef const T*	const_iterator;

	/// Default ctor
	Vector() :
	_size(0)
	{
		return;
	}

	/// Ctor with predefined size
	Vector(pl_uint32 size) :
	_size(0)
	{
		resize(size);
		return;
	}

	/// Copy ctor
	Vector(const Vector& vector):
	_size(0)
	{
		resize(vector._size);

		pl_uint32 i;
		for (i = 0; i < _size; ++i)
		{
			_data[i] = vector[i];
		}
		return;
	}

	/// operator =
	Vector& operator=(const Vector& vector)
	{
		if (this == &vector)
		{
			;
		}
		else
		{
			resize(vector.size());

			pl_uint32 i;
			for (i = 0; i < _size; ++i)
			{
				_data[i] = vector[i];
			}
		}

		return (*this);
	}

	/// Size of array in items
	inline pl_uint32 size() const
	{
		return _size; 
	}

	/// Size of array in bytes
	inline pl_uint32 sizeBytes() const
	{
		return _size*sizeof(T); 
	}

	/// Get reference
	T& at(pl_uint32 pos)
	{
		PL_ASSERTE(pos < _size, PL_T("Index out of range"));
		return *(_data + pos);
	}
	/// Get const reference
	const T& at(pl_uint32 pos) const
	{
		PL_ASSERTE(pos < _size, PL_T("Index out of range"));
		return *(_data + pos);
	}
	/// Get last item
	T& back()
	{
		PL_ASSERTE(_size > 0, PL_T("Vector is empty"));
		return *(_data + _size - 1);
	}
	/// Get last item (const version)
	const T& back() const
	{
		PL_ASSERTE(_size > 0, PL_T("Vector is empty"));
		return *(_data + _size - 1);
	}
	/// Get first item
	iterator begin()
	{
		return (_size == 0 ? 0 : _data);
	}
	/// Get first item (const version)
	const_iterator begin() const
	{
		return (_size == 0 ? 0 : _data);
	}
	/// Get capacity
	inline pl_uint32 capacity() const
	{
		return N; 
	}
	/// Get last iterator
	iterator end()
	{
		return (_size == 0 ? 0 : _data + _size);
	}
	/// Get last iterator (const version)
	const_iterator end() const
	{
		return (_size == 0 ? 0 : _data + _size);
	}
	/// Get capacity in bytes
	inline pl_uint32 capacityBytes() const
	{
		return N*sizeof(T); 
	}
	/// Remove all items from vector
	inline void clear()
	{
		_size = 0; 
		return;
	}
	/// Remove any item from vector
	inline void erase(pl_uint32 pos)
	{
		erase(pos, 1);
		return;
	}

	/// Remove N items from vector
	inline void erase(pl_uint32 offset, pl_uint32 count)
	{
		if (count == 0 || offset + count > _size || count > _size || offset > _size)
		{
			PL_ASSERTW(FALSE, PL_T("Index out of range"));
			return;
		}

		T* pFrom = _data + offset + count;
		T* pTo = _data + offset;
		T* pEnd = _data + _size;

		for (; pFrom < pEnd; pFrom++, pTo++)
		{
			*pTo = *pFrom;
		}
		_size-=count;
	}
	/// Remove item by iterator
	iterator erase(const_iterator where)
	{
		PL_ASSERTE((begin() <= where) && (where < end()), PL_T("Incorrect Iterator"));

		const pl_uint32 pos = static_cast<pl_uint32>(where - begin());
		pl_uint32 i;
		for (i = pos; i < _size - 1; ++i)
		{
			_data[i] = _data[i + 1];
		}

		--_size;

		return (_size == 0 ? 0 : &_data[pos]);
	}
	/// Get first element
	T& front()
	{
		PL_ASSERTE(_size > 0, PL_T("Vector is empty"));

		return *(_data);
	}
	/// Get forst element (const version)
	const T& front() const
	{
		PL_ASSERTE(_size > 0, PL_T("Vector is empty"));

		return *(_data);
	}

	/// Insert new item
	iterator insert(iterator where, const T& value)
	{
		PL_ASSERTE((begin() <= where) && (where <= end()), PL_T("Incorrect Iterator"));

		T copyValue = value;
		pl_int32 pos = static_cast<pl_int32>(where - begin());
		resize(_size + 1);
		pl_int32 i = _size-1;
		while ( i > pos )
		{
			_data[i] = _data[i - 1];
			i--;
		}
		_data[pos] = copyValue;
		return (_data + pos);
	}

	/// Push new item
	inline bool pushBack(const T& item)
	{
		if (_size >= N)
		{
			PL_ASSERTE(FALSE, PL_T("Vector overflow"));
			return false;
		}
		_data[_size++] = item;
		return true;
	}
	
	/// Pop item
	inline void popBack()
	{
		if (_size > 0)
		{
			--_size;
		}
	}

	/// Resize vector
	void resize(pl_uint32 size, const T& value = T())
	{
		if (_size >= N)
		{
			PL_ASSERTW(FALSE, PL_T("New size exceeds Vector size"));
			return;
		}
		if (_size < size)
		{
			pl_uint32 i;
			for (i = _size; i < size; ++i)
			{
				_data[i] = value;
			}
		}

		_size = size;

		return;
	}

	/// Check is vector empty
	inline bool empty() const
	{
		return _size == 0; 
	}

	/// Checkis vector full
	inline bool isFull() const
	{
		return _size >= N; 
	}

	/// Query top item without removing it
	inline bool peekBack(T& item) const
	{
		if (_size <= 0)
		{
			PL_ASSERTW(FALSE, PL_T("Vector is empty"));
			return false;
		}
		item = _data[_size-1];
		return true;
	}

	/// Get item
	T& operator [](pl_uint32 index)
	{
		PL_ASSERTE(index < _size, PL_T("Index out of range")); 
		return *(_data + index); 
	}

	/// get const item
	const T& operator [](pl_uint32 index) const
	{
		PL_ASSERTE(index < _size, PL_T("Index out of range")); 
		return *(_data + index);
	}

	/// Get pointer to array
	const T* get() const
	{
		return _data;
	}

	/// Get pointer to array
	const T* operator +(int offset)
	{
		PL_ASSERTE(offset >=0 && offset < (int)_size, PL_T("Index out of range")); 
		return (_data + offset); 
	}

	/// Get pointer to array
	const T* operator +(pl_uint32 offset)
	{
		PL_ASSERTE(offset < _size, PL_T("Index out of range")); 
		return (_data + offset); 
	}

	/// Find an item
	pl_uint32 find(const T& ref) const
	{
		pl_uint32 i;
		for (i = 0; i < _size; ++i)
		{
			if (_data[i] == ref)
			{
				return i;
			}
		}
		return (pl_uint32)-1;
	}

	/// Sorring
	void sort(int (__cdecl *compare)(const void *, const void *))
	{
		qsort(_data, (size_t)_size, sizeof(T), compare);
	}

private:
	T _data[N];
	pl_uint32 _size;
};

} // namespace pl

//
// End of file 'pulsar_arrayfunc.h'
//
